/*
* Copyright 2010 Competentum group
*
* Licensed under the GNU General Public License, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.gnu.org/licenses/gpl.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.competentum.lib.engines
{
	import com.competentum.lib.events.CommandEvent;
	
	import flash.events.EventDispatcher;

	/**
	 * Base class for all AbstractEngines of a chain of responsibility 
	 * @author Demidov
	 * 
	 */	
	public class AbstractEngine extends EventDispatcher
	{
		public static const TYPE:String = "AbstractEngine";
		
		private var _type:String;

		public function get type():String
		{
			return _type;
		}
		
		private var handableCommandsList:Vector.<String> = new Vector.<String>();
		
		private var _next:AbstractEngine = null;
		
		public function get next():AbstractEngine{
			
			return _next;
		}
		
		public function AbstractEngine(type:String = null)
		{
			super();
			
			if (type) {
				_type = type;
			} else {
				_type = TYPE;
			}
			addHandableCommand(type);
		}
		
		public function addEngine(engine:AbstractEngine):void{
			
			if(next){
				next.addEngine(engine);
			}
			else{
				_next = engine;
			}
		}
		
		/**
		 * Internal var for correct work of <code>activatedEngines</code> list. 
		 * When some engine handles an event, it compares it with current event. If they are different,
		 * then it means that new event hase come and we need to reset <code>activatedEngines</code> list
		 */		
		private static var currentEvent:CommandEvent;
		
		private static var _activatedEngines:Vector.<AbstractEngine> = new Vector.<AbstractEngine>();
		/**
		 * Contains list of all engines that handled last event.
		 * @return 
		 * 
		 */
		public static function get activatedEngines():Vector.<AbstractEngine>
		{
			return _activatedEngines;
		}

		public static function set activatedEngines(value:Vector.<AbstractEngine>):void
		{
			_activatedEngines = value;
		}

		public function addChainListener(handler:Function):void
		{
			addEventListener(CommandEvent.ACTIVATE_ENGINE, handler);
			if (next)
			{
				next.addChainListener(handler);
			}
		}
		
		/**
		 * AbstractEngines chain the receiving CommandEvent 
		 * and pass the request along the chain until an Engine handles it.
		 * @param event
		 * @return AbstractEngine (first of all that handle this event). List of all activated engines 
		 * can be accessed by using static property <code>activatedEngines</code>
		 */		
		public function handle(event:CommandEvent):AbstractEngine{
			if (event != currentEvent) {
				// new chain started. reset activated engines list
				currentEvent = event;
				activatedEngines.length = 0;
			}
			if(isCommandHandable(event.commandType)){
				activate(event);
				// add to activated engines list
				activatedEngines.push(this);
				if (!event.allowSeveralEngineHandlers) {
					// prevent other engines handle this event.
					return this;
				}
			}
			if(_next){
				return _next.handle(event);
			}
			return activatedEngines.length > 0 ? activatedEngines[0] : null;
		}
		
		/**
		 * Handle CommandEvent
		 * This metod must override in all derived AbstractEngines
		 * @param event
		 */		
		protected function activate(event:CommandEvent = null):void{
			
		}
		
		protected function isCommandHandable(command:String):Boolean{
			if(handableCommandsList.indexOf(command)!=-1){
				return true;
			}
			return false;
		}
		
		protected function addHandableCommand(command:String):void{
			handableCommandsList.push(command);
		} 
	}
}